package com.codemonkey.lq.qd.facotry.config;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

import com.codemonkey.lq.qd.ServiceContext;
import com.codemonkey.lq.qd.builder.BuilderEventListener;
import com.codemonkey.lq.qd.builder.BuilderPostProcessor;
import com.codemonkey.lq.qd.builder.support.MixedBuilderEventListener;
import com.codemonkey.lq.qd.facotry.config.executer.ServiceHandler;

/**
 * 全称 Service Configuration. 为了与mybatis的Configuration进行区分.所以加上 S 前缀来区分
 * @author lq
 *
 */
public class SConfiguration {
	//Configuration  d;

	public static final String DEFAULT_CONFIG_FILE_NAME = "sd_config.xml";

	private final Map<String, ServiceDefinition> serviceContainer = new HashMap<String, ServiceDefinition>(
			500);
	private final Set<BuilderPostProcessor> builderPostProcessores = new HashSet<BuilderPostProcessor>(
			5);
	private final Set<ServiceHandler> serviceHandlers = new HashSet<ServiceHandler>(5);

	/**
	 * 已加载的Service Resource
	 */
	private final Set<String> loadedServiceResources = new HashSet<String>();

	/**
	 * Execute Context Moifier ; 执行上下文的编辑者,给予第三方一个机会来修改Service在执行时的Context
	 */
	protected ExecuteContextModifier ecModifier = null;

	/**
	 * 执行本次SQL操作时的MyBatis相关的sqlSessionFactory;用于多数据源,目前未实现多数据源
	 */
	protected SqlSessionFactorySelecter sqlSessionFactorySelecter = null;

	/**
	 * builder 相关事件的监听器
	 */
	protected MixedBuilderEventListener builderEventListener = new MixedBuilderEventListener();

	/**
	 * Execute 时相关的事件监听器
	 */
	protected List<ExecuteEventListener<ExecutionEvent>> executeEventListeners = new ArrayList<ExecuteEventListener<ExecutionEvent>>(
			10);

	/**
	 * 配置文件中<properties></properties>中的相关信息
	 */
	protected Properties variables = new Properties();

	/**
	 * ctor
	 */
	public SConfiguration() {
		super();
	}

	// ------------------------ Properties
	// -----------------------------------------------------------------------------
	public SqlSessionFactorySelecter getSqlSessionFactorySelecter() {
		if (null == sqlSessionFactorySelecter) {
			throw new IllegalArgumentException(
					"must set 'SqlSessionFactorySelecter' property which definition in "
							+ this.getClass().getSimpleName());
		}
		return sqlSessionFactorySelecter;
	}

	public void setSqlSessionFactorySelecter(SqlSessionFactorySelecter selecter) {
		this.sqlSessionFactorySelecter = selecter;
	}

	// -----------------------------------------------------------------------------
	public ExecuteContextModifier getExecuteContextModifier() {
		if (null == ecModifier) {
			ecModifier = new DefaultExecuteContextModifier();
		}
		return ecModifier;
	}

	public void setExecuteContextModifier(ExecuteContextModifier ecModifier) {
		this.ecModifier = ecModifier;
	}

	public Properties getVariables() {
		return variables;
	}

	public void setVariables(Properties variables) {
		this.variables = variables;
	}

	// ------------------------ LoadedResource
	public void addLoadedResource(String loadedServiceResource) {
		loadedServiceResources.add(loadedServiceResource);
	}

	public boolean isResourceLoaded(String loadedServiceResource) {
		return loadedServiceResources.contains(loadedServiceResource);
	}

	protected boolean removeLoadedResource(String loadedServiceResource) {
		return loadedServiceResources.remove(loadedServiceResource);
	}

	// ------------------------ ServiceDefinition
	public void addServiceDefinition(ServiceDefinition sd) {
		String key = sd.getId();
		if (serviceContainer.containsKey(key)) {
			String errorInfo = String.format("this service which named '%s' has exist ", key);
			throw new IllegalArgumentException(errorInfo);
		}
		serviceContainer.put(key, sd);
	}

	public ServiceDefinition getServiceDefinition(String sdId) {
		return serviceContainer.get(sdId);
	}

	public Collection<ServiceDefinition> getServiceDefinitionGroup() {
		return Collections.unmodifiableCollection(serviceContainer.values());
	}

	// ------------------------ BuilderPostProcessor
	public void addBuilderPostProcessor(BuilderPostProcessor bpp) {
		builderPostProcessores.add(bpp);
	}

	public Collection<BuilderPostProcessor> getBuilderPostProcessorGroup() {
		return Collections.unmodifiableCollection(builderPostProcessores);
	}

	// ------------------------ ServiceHandler
	public void addServiceHandler(ServiceHandler sh) {
		serviceHandlers.add(sh);
	}

	public Collection<ServiceHandler> getServiceHandlerGroup() {
		return Collections.unmodifiableCollection(serviceHandlers);
	}

	// ---------------------------
	public BuilderEventListener getBuilderEventListener() {
		return builderEventListener;
	}

	public void addBuilderEventListener(BuilderEventListener builderEventListener) {
		this.builderEventListener.addListener(builderEventListener);
	}

	//-----------------------------

	public void addExecuteEventListener(ExecuteEventListener listener) {
		executeEventListeners.add(listener);
	}

	public Collection<ExecuteEventListener<ExecutionEvent>> getExecuteEventListeners() {
		return Collections.unmodifiableList(executeEventListeners);
	}

	// ------------------------------------------------------------------------------------
	//               PUBLIC STATIC INNER INTERFACE
	// ------------------------------------------------------------------------------------	

	/**
	 * 这里我们是否应该提供一个默认实现,毕竟上面的实现应该属于框架规定的,然后子类可以选择覆盖之
	 */
	public static class DefaultExecuteContextModifier implements ExecuteContextModifier {
		@Override
		public void modify(Map<String, Object> executeContext, ServiceContext sc,
				ActionDefinition currentAD) {
			String parameter = currentAD.getParameter();
			if (parameter != null) {
				String[] parameterArray = parameter.split("\\+");
				for (String p : parameterArray) {
					if (p.startsWith("${")) { // 从之前的Action执行结果中取
						String paramName = p.replace("${", "").replace("}", "");
						String realName = paramName;
						String aliasName = paramName;
						if (paramName.indexOf("@") > -1) {
							String[] nameArr = paramName.split("@");
							realName = nameArr[0];
							aliasName = nameArr[1];
						}
						// 从ActionResultGroup容器中取出来,推入到ExecuteContext容器中.
						Object beforeActionResult = sc.getActionResultContainer().get(realName);
						sc.getExecuteContextContainer().put(aliasName, beforeActionResult);
					}
				}
			}

		}

	}

}
